<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        let data = {
            text: 'xiaobai',
            ok: true,
            foo: 1
        }
        let activeAffect
        let effectStack = []
        let bucket = new WeakMap()
        let obj = new Proxy(data, {
            get(target, key) {
                debugger
                track(target, key)
                console.log('读取了', key);
                return target[key]
            },
            set(target, key, newVal) {
                target[key] = newVal
                trigger(target, key)
                return true
            }
        })
        function track(target, key) {
            debugger
            let depMap = bucket.get(target)
            if (!depMap) {
                bucket.set(target, (depMap = new Map()))
            }
            let deps = depMap.get(key)
            if (!deps) {
                depMap.set(key, (deps = new Set()))
            }
            deps.add(activeAffect)
            // 在get读取变量的时候就要这么去操作
            // 新增 把deps副作用集合添加到activeAffect变量中去
            activeAffect.deps.push(deps)
        }
        function trigger(target, key) {
            debugger
            let depMap = bucket.get(target)
            if (!depMap) return
            let effects = depMap.get(key)
            if (!effects) return
            // （避免无限循环）创建一个新的set 数据是effects 然后遍历执行它
            // 如果直接effects 然后遍历执行


            // let newSet = new Set(effects)
            // newSet.forEach(effect => effect())
            let effectsToRun = new Set()
            effects && effects.forEach(effectFn => {
                // 修改了这里 如果说当前执行的副作用函数和现在要添加的副作用函数是一致的 就不添加到set结构里面去 就不会被执行
                if (effectFn !== activeAffect) {
                    effectsToRun.add(effectFn)
                }
            })
            effectsToRun.forEach(effect => effect())
        }

        function effect(fn) {
            const effectFn = () => {
                debugger
                // 执行前 先清除当前的副作用函数 然后
                cleanUp(effectFn)
                activeAffect = effectFn
                effectStack.push(effectFn)
                // console.log('打印fn变量', fn);
                fn()
                effectStack.pop()
                // 目的就是 内层副作用函数执行完毕后 能够弹出栈 然后activeAffect能够指向更新为外层的副作用函数
                activeAffect = effectStack[effectStack.length - 1]
            }
            effectFn.deps = []
            effectFn()
        }
        function cleanUp(effectFn) {
            debugger
            for (let i = 0; i < effectFn.deps.length; i++) {
                let deps = effectFn.deps[i]
                // deps是一个集合 deps.delete是从集合中删除 effectFn函数
                // 而for循环 则是从所有的集合中删除effectFn函数
                deps.delete(effectFn)
            }
            effectFn.deps.length = 0
        }
        // 修改obj.ok的时候 只有effectFn2执行
        // 修改obj.text的时候 effectFn1和effectFn2同时执行
        // 我们修改obj.text试试看 -> 我们发现 effectFn2执行了，但是我们更加希望effectFn1执行如果不是的话就是错误的 -> 因为内层的副作用函数会覆盖外层的副作用函数 effectFn2覆盖了effectFn1 所以即便我们修改了obj.text 也是执行obj.ok
        effect(function () {
            obj.foo = obj.foo + 1
        })
    </script>
</body>

</html>